package com.miniserver.tql;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.miniserver.tql.database.Configuration;
import com.miniserver.tql.database.DatabaseEngine;
import com.miniserver.tql.database.ModelConfiguration;
import com.miniserver.tql.database.impl.ConfigurationImpl;
import com.miniserver.tql.database.impl.DatabaseEngineBaseImpl;
import com.miniserver.tql.database.impl.ModelConfigurationImpl;
import com.miniserver.tql.database.impl.MySQLEngineImpl;
import com.miniserver.tql.database.impl.OracleEngineImpl;
import com.miniserver.tql.database.impl.PostgreSQLEngineImpl;
import com.miniserver.tql.impl.HsqlMaster;
import com.miniserver.util.AppConfiguration;
import com.miniserver.util.BussinessException;
import com.miniserver.util.DKeyDesEncrypt;
import com.miniserver.util.LogUtil;

public final class TqlGlobal {
	private static LogUtil log = LogUtil.getLogUtil("TqlGlobal");
	private static Object lock = new Object();
	private static TqlMaster tqlMaster = null;
	private static Configuration config = null;
	private static ConcurrentHashMap<String, DatabaseEngine> engines = new ConcurrentHashMap<String, DatabaseEngine>();
	private static ConcurrentHashMap<String, ModelConfiguration> modelConfigs = new ConcurrentHashMap<String, ModelConfiguration>();
	private static long modelConfigCheckInterval = 60 * 1000;
	private static ConcurrentHashMap<String, Long> modelupdateTimeMap = new ConcurrentHashMap<String, Long>();
	private static Thread initThread = null;
	private static HashMap<String,ArrayList<String>> ipModelWhiteMap= new HashMap<String,ArrayList<String>>();
	private static HashMap<String,ArrayList<String>> ipMethodWhiteMap= new HashMap<String,ArrayList<String>>();
	
	private static JSONArray stateJson = new JSONArray();
	private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	private static final int WAITING_INIT = 0;
	private static final int INITIALIZING_MODEL = 10;
	private static final int INITIALIZING_MODEL_SUCCESS = 11;
	private static final int INITIALIZING_MODEL_FAIL = 12;
	private static final int INITIALIZING_DATA= 20;
	private static final int INITIALIZING_DATA_SUCCESS = 21;
	private static final int INITIALIZING_DATA_FAIL = 22;
	
	public static final int INIT_SUCCESS = 30;
	public static int status = WAITING_INIT;

	public static final String CK_PHYSICAL_DATABASE = "physicaldb";
	public static final String CK_MOMERY_DATABASE = "memdb";

	public static Configuration getConfig() throws Exception {
		return config;
	}

	public static ModelConfiguration getModelConfig(String modelName) throws Exception {
		if (!TqlGlobal.modelConfigs.containsKey(modelName)) {
			throw new Exception("Model配置文件不存在:" + modelName);
		} else {
			return TqlGlobal.modelConfigs.get(modelName);
		}
	}
	
	public static HashMap<String, ArrayList<String>> getIpMethodWhiteMap(){
		return TqlGlobal.ipMethodWhiteMap;
	}

	public static HashMap<String, ArrayList<String>> getIpModelWhiteMap(){
		return TqlGlobal.ipModelWhiteMap;
	}
	
	public static DatabaseEngine getEngine(String engineType) throws Exception {
		synchronized (lock) {
			switch(TqlGlobal.status) {
	    	case WAITING_INIT:
				throw new BussinessException("-1", "正在启动内存数据库...");
	    	case INITIALIZING_MODEL:
				throw new BussinessException("-1", "正在初始化model...");
	    	case INITIALIZING_MODEL_FAIL:
				throw new BussinessException("-1", "初始化model失败...");
	    	case INITIALIZING_MODEL_SUCCESS:
				throw new BussinessException("-1", "初始化model成功，等待初始化数据...");
	    	case INITIALIZING_DATA:
				throw new BussinessException("-1", "正在初始化数据...");
	    	case INITIALIZING_DATA_FAIL:
				throw new BussinessException("-1", "初始化数据失败...");
	    	default:
	    		break;
	    	}
			
			if (!TqlGlobal.engines.containsKey(engineType)) {
				throw new BussinessException("-1", "不支持的DatabaseEngine类型:" + engineType);
			} else {
				return TqlGlobal.engines.get(engineType);
			}
		}
	}

	public static TqlMaster getTqlMaster() throws Exception {
		return TqlGlobal.tqlMaster;
	}
	
	public static ConcurrentHashMap<String, ModelConfiguration> getModelConfiguration() throws Exception{
		return TqlGlobal.modelConfigs;
	}
	
	private static JSONObject getStatusInfo (int status, String description) {
		JSONObject obj = new JSONObject();
		obj.put("status", status);
		obj.put("description", description);
		obj.put("dateStr", dateFormat.format(new Date()));
		
		return obj;
	}

	public static void init(final String webRoot) {
		try {
			stateJson.add(getStatusInfo(WAITING_INIT, "开始时间"));
			stateJson.add(getStatusInfo(INITIALIZING_MODEL, "初始化Model开始时间"));
			TqlGlobal.status = INITIALIZING_MODEL;
			TqlGlobal.config = new ConfigurationImpl(webRoot,"/WEB-INF/data/data.cfgbin");
			TqlGlobal.loadModels();
			stateJson.add(getStatusInfo(INITIALIZING_MODEL_SUCCESS, "初始化Model结束时间"));
			TqlGlobal.status = INITIALIZING_MODEL_SUCCESS;
//			loadIpWhiteList();//载入ip白名单
		} catch (Exception ex) {
			log.error("tql初始化失败", ex);
			stateJson.add(getStatusInfo(INITIALIZING_MODEL_FAIL, "初始化Model失败时间"));
			TqlGlobal.status = INITIALIZING_MODEL_FAIL;
			return;
		}
		//
		initThread = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					try {
						stateJson.add(getStatusInfo(INITIALIZING_DATA, "初始化数据开始时间"));
						TqlGlobal.status = INITIALIZING_DATA;
						doInit(webRoot);
						stateJson.add(getStatusInfo(INITIALIZING_DATA_SUCCESS, "初始化数据结束时间"));
						TqlGlobal.status = INITIALIZING_DATA_SUCCESS;
					} catch (Exception ex) {
						log.error("tql初始化线程执行失败", ex);
						stateJson.add(getStatusInfo(INITIALIZING_DATA_FAIL, "初始化数据失败时间"));
						TqlGlobal.status = INITIALIZING_DATA_FAIL;
					}
					stateJson.add(getStatusInfo(INIT_SUCCESS, "结束时间"));
					TqlGlobal.status = INIT_SUCCESS;
					System.out.println("server init ok");
				}
				while (true) {
					try {
						Thread.sleep(modelConfigCheckInterval);
						// 重新加载修改的model
						checkModels();
					} catch (InterruptedException e) {
					}
				}
			}
		});
		initThread.start();
	}

	/*载入ip白名单*/
	private static void loadIpWhiteList() {
		String modelStr=AppConfiguration.getParameter("ip_white_list_model");
		String methodStr=AppConfiguration.getParameter("ip_white_list_method");
		String[] modelArray=modelStr.split(";");
		String[] methodArray=methodStr.split(";");
		for(String model:modelArray){
			String[] m=model.split(":");
			String modelKey=m[0];
			ArrayList<String> modelList=new ArrayList<String>();
			for(String ip:m[1].split("\\$\\$")){
				modelList.add(ip);
			}
			TqlGlobal.ipModelWhiteMap.put(modelKey,modelList);
		}
		for(String method:methodArray){
			String[] m=method.split(":");
			String methodKey=m[0];
			ArrayList<String> methodList=new ArrayList<String>();
			for(String ip:m[1].split("\\$\\$")){
				methodList.add(ip);
			}
			TqlGlobal.ipMethodWhiteMap.put(methodKey,methodList);
		}
	}

	private static void doInit(String webRoot) throws Exception {
		// tqlMaster的初始化比较耗时, 必须放到工作者线程里执行
		TqlGlobal.tqlMaster = new HsqlMaster();
		TqlGlobal.tqlMaster.init(TqlGlobal.config);
		//
		TqlGlobal.createEngine(TqlGlobal.tqlMaster);
	}

	private static void createEngine(TqlMaster tqlMaster) {
		String[] dbNames = TqlGlobal.config.getParamValue("physicaldb").split(",");
		for (String s : dbNames) {
			String dbName = s.trim();
			DatabaseEngine engine = null;
			String engineType = TqlGlobal.config.getParamValue(dbName + ".engine");
			if (engineType.equalsIgnoreCase("postgres")) {
				engine = new PostgreSQLEngineImpl();			
			} else if (engineType.equalsIgnoreCase("oracle")) {
				engine = new OracleEngineImpl();				
			} else if (engineType.equalsIgnoreCase("mysql")) {
				engine = new MySQLEngineImpl();				
			} else {
				engine = new DatabaseEngineBaseImpl();				
			}
			//
			if (engine != null) {
				TqlGlobal.engines.put(dbName, engine);
			}
		}
	}

	/**
	 * 重新加载修改的model
	 */
	private static void checkModels() {
		String[] modelNames = TqlGlobal.config.getParamValue("models").split(",");
		long modifyTime = 0L;
		for (String s : modelNames) {
			String model = s.trim();
			String modelFileName = TqlGlobal.config.getWebRoot() + TqlGlobal.config.getParamValue(model);
			if (modelFileName.endsWith(".xml"))
			{
				// 实现tml自动部署功能
				File file = new File(modelFileName);
				if(file.exists()) {
					File binFile = new File(modelFileName.concat("bin"));
	       			if (binFile.exists()) {
	       				binFile.delete();
	       			}
	       			DKeyDesEncrypt.encryptFile(file, binFile);
	       			file.delete();
				}
       			// end
				
				modelFileName = modelFileName.concat("bin");
			}
			modifyTime = (new File(modelFileName)).lastModified();
			if (modifyTime > modelupdateTimeMap.get(model)) {
				ModelConfiguration config = new ModelConfigurationImpl(TqlGlobal.config.getWebRoot());
				try {
					config.load(modelFileName);
					TqlGlobal.modelConfigs.put(model, config);
					modelupdateTimeMap.put(model, modifyTime);					
					log.debug(String.format("model文件 %s 已完成重载",s));
				} catch (Exception e) {
					log.error(String.format("model %s 加载失败",s),e);
				}
			}
		}
		
	}

	private static void loadModels() throws Exception {
		String[] modelNames = TqlGlobal.config.getParamValue("models").split(",");
		long modifyTime = 0L;
		for (String s : modelNames) {
			String model = s.trim();
			String modelFileName = TqlGlobal.config.getWebRoot() + TqlGlobal.config.getParamValue(model);
			ModelConfiguration config = new ModelConfigurationImpl(TqlGlobal.config.getWebRoot());
			if (modelFileName.endsWith(".xml"))
			{
				modelFileName = modelFileName.concat("bin");
			}
			config.load(modelFileName);
			TqlGlobal.modelConfigs.put(model, config);
			modifyTime = (new File(modelFileName)).lastModified();
			modelupdateTimeMap.put(model, modifyTime);
		}
	}

	public static String handleDatabaseRequest(String modelName, HashMap<String, String> values) throws Exception {
	    synchronized(lock) {
	    	switch(TqlGlobal.status) {
	    	case WAITING_INIT:
				throw new BussinessException("-1", "正在启动内存数据库...");
	    	case INITIALIZING_MODEL:
				throw new BussinessException("-1", "正在初始化model...");
	    	case INITIALIZING_MODEL_FAIL:
				throw new BussinessException("-1", "初始化model失败...");
	    	case INITIALIZING_MODEL_SUCCESS:
				throw new BussinessException("-1", "初始化model成功，等待初始化数据...");
	    	case INITIALIZING_DATA:
				throw new BussinessException("-1", "正在初始化数据...");
	    	case INITIALIZING_DATA_FAIL:
				throw new BussinessException("-1", "初始化数据失败...");
	    	default:
	    		break;
	    	}
		}
		ModelConfiguration config = TqlGlobal.getModelConfig(modelName);
		String physicaldb = config.getParamValue(CK_PHYSICAL_DATABASE);
		DatabaseEngine engine = (DatabaseEngine) TqlGlobal.getEngine(physicaldb).clone();
		return engine.handleRequest(config, values);
	}

	public static void destory() {
		if (initThread!=null)
		{
			initThread.interrupt();
		}		
	}
	
	/**
	 * 初始化的状态和每个状态的时间信息
	 * @return
	 */
	public static String getStateInfo() {
		return stateJson.toString();
	}
}
